#include "KeyCountServer.h"

namespace KeyCountServer {

ClientNet::ClientNet(Client *client, int clientId)
{
    m_client    = client;
    m_clientId  = clientId;
    m_iow       = new IOWrapper();
    m_local     = new ClientLocal();
    m_online    = 0;
}

bool ClientNet::handle()
{
//    LOG("Client %d//handle a new packet\n", m_clientId);
    m_iow->setFd(m_client->m_socket->m_s);
    
    if (!getHead())
    {
        LOG("Client %d//get head error\n", m_clientId);
        delUser();
        return 0;
    }
    if (m_msghGet.cmd <= 0 || m_msghGet.cmd > UPLIMIT)
    {
        LOG("Client %d//unkown command\n", m_clientId);
        return 0;
    }
    if (!getMsg())
    {
        LOG("Client %d//get msg error\n", m_clientId);
        delUser();
        return 0;
    }
    
    switch(m_msghGet.cmd)
    {
    case REGISTER:
        LOG("Client %d//Register command\n", m_clientId);
        doRegister();
        break;
    case LOGIN:
        LOG("Client %d//Login command\n", m_clientId);
        doLogin();
        break;
    case UPLOAD:
        LOG("Client %d//Upload command\n", m_clientId);
        doUpload();
        break;
    case DOWNLOAD:
        LOG("Client %d//Download command\n", m_clientId);
        doDownload();
        break;
    case LOGOUT:
        LOG("Client %d//Logout command\n", m_clientId);
        doLogout();
        break;
    default:
        LOG("Client %d//unkown command\n", m_clientId);
        delUser();
        return 0;
    }
    
    if (!putHead())
    {
        LOG("Client %d//put head error\n", m_clientId);
        delUser();
        return 0;
    }
    if (!putMsg())
    {
        LOG("Client %d//put msg error\n", m_clientId);
        delUser();
        return 0;
    }
    
    if (m_msghGet.cmd == REGISTER || m_msghGet.cmd == LOGOUT)
        return 0;
    return 1;
}

bool ClientNet::getHead()
{
    bool    rc = 1;
    fd_set  rSet;
    timeval timeout;
    int     fd = m_client->m_socket->m_s;
    
    timeout.tv_sec  = 60;
    timeout.tv_usec = 0;
    FD_ZERO(&rSet);
    FD_SET(fd, &rSet);
    
    int nReady = select(fd + 1, &rSet, NULL, NULL, &timeout);
    if (nReady <= 0)
        return 0;
    
    rc &= (m_iow->readN(&m_msghGet.cmd, sizeof(int)) > 0);
    rc &= (m_iow->readN(&m_msghGet.length, sizeof(int)) > 0);
    rc &= (m_iow->readN(&m_msghGet.error, sizeof(int)) > 0);
    return rc;
}

bool ClientNet::getMsg()
{
    if (m_msghGet.length == 0)
        return 1;
    Debug(("len = %d", m_msghGet.length));
    
    fd_set  rSet;
    timeval timeout;
    int     fd = m_client->m_socket->m_s;
    
    timeout.tv_sec  = 60;
    timeout.tv_usec = 0;
    FD_ZERO(&rSet);
    FD_SET(fd, &rSet);
    
    int nReady = select(fd + 1, &rSet, NULL, NULL, &timeout);
    if (nReady <= 0)
        return 0;
    
    m_msgGet = new char[m_msghGet.length + 1];
    int rc = m_iow->readN(m_msgGet, m_msghGet.length);
    if (rc <= 0)
        return 0;
    m_msgGet[m_msghGet.length] = 0;
    return 1;
}

bool ClientNet::putHead()
{
    bool rc = 1;
    
    rc &= (m_iow->writeN(&m_msghPut.cmd, sizeof(int)) > 0);
    rc &= (m_iow->writeN(&m_msghPut.length, sizeof(int)) > 0);
    rc &= (m_iow->writeN(&m_msghPut.error, sizeof(int)) > 0);
    return rc;
}

bool ClientNet::putMsg()
{
    if (m_msghPut.length == 0)
        return 1;
    int rc = (m_iow->writeN(m_msgPut, m_msghPut.length) > 0);
    
    return rc;
}

void ClientNet::doRegister()
{
    sscanf(m_msgGet, "%s\n%s", m_username, m_password);
    m_msghPut.cmd       = REGISTER_ACK;
    m_msghPut.length    = 0;
    m_msghPut.error     = (int)m_local->isExist(m_username, m_password);
    
    if (m_msghPut.error == 0)
    {
        m_local->createUser(m_username, m_password);
        LOG("Client %d//User %s Register success\n", m_clientId, m_username);
        return;
    }
    LOG("Client %d//User %s Register fail\n", m_clientId, m_username);
}

void ClientNet::doLogin()
{
    sscanf(m_msgGet, "%s\n%s", m_username, m_password);
    m_msghPut.cmd       = LOGIN_ACK;
    m_msghPut.length    = 0;
    char password[MAX_LEN] = {0};
    bool rc = m_local->isExist(m_username, password);
    
    if (!rc)
    {
        m_msghPut.error = 1;
        LOG("Client %d//Login fail. User %s not exist\n", m_clientId, m_username);
        return;
    }
    if (strcmp(password, m_password))
    {
        m_msghPut.error = 2;
        LOG("Client %d//Login fail. User %s passwd wrong\n", m_clientId, m_username);
        return;
    }
    if (!addUser())
    {
        m_msghPut.error = 3;
        LOG("Client %d//Login fail. User %s is already online\n", m_clientId, m_username);
        return;
    }
    m_msghPut.error = 0;
    m_online        = 1;
    m_local->loadData(m_username, m_msgPut, &m_msghPut.length);
    LOG("Client %d//Login success. User %s\n", m_clientId, m_username);
}

void ClientNet::doUpload()
{
    m_msghPut.cmd       = UPLOAD_ACK;
    m_msghPut.length    = 0;
    m_msghPut.error     = !m_local->saveData(m_username, m_msgGet, m_msghGet.length);
    LOG("Client %d//Upload success. User %s, len %d\n", m_clientId, m_username, m_msghGet.length);
}

void ClientNet::doDownload()
{
    m_msghPut.cmd       = DOWNLOAD_ACK;
    m_msghPut.error     = !m_local->loadData(m_username, m_msgPut, &m_msghPut.length);
    LOG("Client %d//Download success. User %s, len %d\n", m_clientId, m_username, m_msghPut.length);
}

void ClientNet::doLogout()
{
    m_msghPut.cmd       = LOGOUT_ACK;
    m_msghPut.length    = 0;
    m_msghPut.error     = !m_local->saveData(m_username, m_msgGet, m_msghGet.length);
    delUser();
    LOG("Client %d//Logout success. User %s\n", m_clientId, m_username);
}

void ClientNet::delUser()
{
    if (m_online == 0)
        return;
    
    ThreadGuard guard(m_client->m_server->m_mutex);
    std::set<std::string> &onlineList = m_client->m_server->m_onlineList;
    
    onlineList.erase(m_username);
    m_online = 0;
}

bool ClientNet::addUser()
{
    ThreadGuard guard(m_client->m_server->m_mutex);
    std::set<std::string> &onlineList = m_client->m_server->m_onlineList;
    
    if (onlineList.count(m_username))
        return 0;
    onlineList.insert(m_username);
    return 1;
}

}
